#include "application.h"
#include "assets/lang_config.h"
#include "audio_codec.h"
#include "board.h"
#include "display.h"
#include "font_awesome_symbols.h"
#include "iot/thing_manager.h"
#include "ml307_ssl_transport.h"
#include "mqtt_protocol.h"
#include "system_info.h"
#include "websocket_protocol.h"

#include <arpa/inet.h>
#include <cJSON.h>
#include <cstring>
#include <driver/gpio.h>
#include <esp_app_desc.h>
#include <esp_log.h>

#include "task_uart.h"

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

#define TAG "Application"

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

static const char *const STATE_STRINGS[] = {
    "unknown",
    "starting",
    "configuring",
    "idle",
    "connecting",
    "listening",
    "speaking",
    "upgrading",
    "activating",
    "fatal_error",
    "invalid_state"};

static uint16_t test_counter = 0;

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

Application::Application() {
  event_group_     = xEventGroupCreate();
  background_task_ = new BackgroundTask(4096 * 8);

  esp_timer_create_args_t clock_timer_args = {
      .callback =
          [](void *arg) {
            Application *app = (Application *)arg;
            app->OnClockTimer();
          },
      .arg                   = this,
      .dispatch_method       = ESP_TIMER_TASK,
      .name                  = "clock_timer",
      .skip_unhandled_events = true};
  esp_timer_create(&clock_timer_args, &clock_timer_handle_);
}

Application::~Application() {
  if (clock_timer_handle_ != nullptr) {
    esp_timer_stop(clock_timer_handle_);
    esp_timer_delete(clock_timer_handle_);
    clock_timer_handle_ = nullptr;
  }
  if (background_task_ != nullptr) {
    delete background_task_;
  }
  vEventGroupDelete(event_group_);
}

/**
 * @brief 检查并处理固件新版本
 *
 * 该函数执行以下操作：
 * 1. 检查是否有可用的新固件版本
 * 2. 如果存在新版本，则执行升级流程（包括设备状态设置、显示更新、音频处理等）
 * 3. 如果没有新版本，则标记当前版本为有效并显示版本信息
 * 4. 处理激活码相关逻辑
 *
 * @note 升级过程会重启设备，升级失败时会显示错误信息并延迟重启
 * @note 函数包含重试机制，最多重试10次，每次间隔60秒
 */
void Application::CheckNewVersion() {
  auto &board   = Board::GetInstance();
  auto  display = board.GetDisplay();
  // Check if there is a new firmware version available
  ota_.SetPostData(board.GetJson());

  const int MAX_RETRY   = 10;
  int       retry_count = 0;

  while (true) {
    if (!ota_.CheckVersion()) {
      retry_count++;
      if (retry_count >= MAX_RETRY) {
        ESP_LOGE(TAG, "Too many retries, exit version check");
        return;
      }
      ESP_LOGW(TAG, "Check new version failed, retry in %d seconds (%d/%d)", 60, retry_count, MAX_RETRY);
      vTaskDelay(pdMS_TO_TICKS(60000));
      continue;
    }
    retry_count = 0;

    if (ota_.HasNewVersion()) {
      Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "happy", Lang::Sounds::P3_UPGRADE);
      // Wait for the chat state to be idle
      do {
        vTaskDelay(pdMS_TO_TICKS(3000));
      } while (GetDeviceState() != kDeviceStateIdle);

      // Use main task to do the upgrade, not cancelable
      Schedule([this, display]() {
        SetDeviceState(kDeviceStateUpgrading);

        display->SetIcon(FONT_AWESOME_DOWNLOAD);
        std::string message = std::string(Lang::Strings::NEW_VERSION) + ota_.GetFirmwareVersion();
        display->SetChatMessage("system", message.c_str());

        auto &board = Board::GetInstance();
        board.SetPowerSaveMode(false);
#if CONFIG_USE_WAKE_WORD_DETECT
        wake_word_detect_.StopDetection();
#endif
        // 预先关闭音频输出，避免升级过程有音频操作
        auto codec = board.GetAudioCodec();
        codec->EnableInput(false);
        codec->EnableOutput(false);
        {
          std::lock_guard<std::mutex> lock(mutex_);
          audio_decode_queue_.clear();
        }
        background_task_->WaitForCompletion();
        delete background_task_;
        background_task_ = nullptr;
        vTaskDelay(pdMS_TO_TICKS(1000));

        ota_.StartUpgrade([display](int progress, size_t speed) {
          char buffer[64];
          snprintf(buffer, sizeof(buffer), "%d%% %zuKB/s", progress, speed / 1024);
          display->SetChatMessage("system", buffer);
        });

        // If upgrade success, the device will reboot and never reach here
        display->SetStatus(Lang::Strings::UPGRADE_FAILED);
        ESP_LOGI(TAG, "Firmware upgrade failed...");
        vTaskDelay(pdMS_TO_TICKS(3000));
        Reboot();
      });

      return;
    }

    // No new version, mark the current version as valid
    ota_.MarkCurrentVersionValid();
    std::string message = std::string(Lang::Strings::VERSION) + ota_.GetCurrentVersion();
    display->ShowNotification(message.c_str());

    if (ota_.HasActivationCode()) {
      // Activation code is valid
      SetDeviceState(kDeviceStateActivating);
      ShowActivationCode();

      // Check again in 60 seconds or until the device is idle
      for (int i = 0; i < 60; ++i) {
        if (device_state_ == kDeviceStateIdle) {
          break;
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
      }
      continue;
    }

    SetDeviceState(kDeviceStateIdle);
    display->SetChatMessage("system", "");
    PlaySound(Lang::Sounds::P3_SUCCESS);
    // Exit the loop if upgrade or idle
    break;
  }
}

/**
 * @brief 显示激活码并播放对应的数字语音
 *
 * 该函数从OTA模块获取激活消息和激活码，先显示激活消息并播放激活提示音，
 * 然后逐个播放激活码中每个数字对应的语音。使用digit_sounds数组映射数字到语音资源。
 *
 * @note 由于Alert语句会占用9KB SRAM，函数会等待1秒确保完成，并等待后台任务完成。
 */
void Application::ShowActivationCode() {
  auto &message = ota_.GetActivationMessage();
  auto &code    = ota_.GetActivationCode();

  struct digit_sound {
    char                    digit;
    const std::string_view &sound;
  };
  static const std::array<digit_sound, 10> digit_sounds{
      {digit_sound{'0', Lang::Sounds::P3_0},
       digit_sound{'1', Lang::Sounds::P3_1},
       digit_sound{'2', Lang::Sounds::P3_2},
       digit_sound{'3', Lang::Sounds::P3_3},
       digit_sound{'4', Lang::Sounds::P3_4},
       digit_sound{'5', Lang::Sounds::P3_5},
       digit_sound{'6', Lang::Sounds::P3_6},
       digit_sound{'7', Lang::Sounds::P3_7},
       digit_sound{'8', Lang::Sounds::P3_8},
       digit_sound{'9', Lang::Sounds::P3_9}}};

  // This sentence uses 9KB of SRAM, so we need to wait for it to finish
  Alert(Lang::Strings::ACTIVATION, message.c_str(), "happy", Lang::Sounds::P3_ACTIVATION);
  vTaskDelay(pdMS_TO_TICKS(1000));
  background_task_->WaitForCompletion();

  for (const auto &digit : code) {
    auto it = std::find_if(
        digit_sounds.begin(), digit_sounds.end(), [digit](const digit_sound &ds) { return ds.digit == digit; });
    if (it != digit_sounds.end()) {
      PlaySound(it->sound);
    }
  }
}

/**
 * @brief 发送警报信息并更新显示
 *
 * 该函数用于在系统中触发警报，将状态、消息和表情显示在屏幕上，
 * 并可选择播放提示音。
 *
 * @param status 警报状态信息
 * @param message 警报消息内容
 * @param emotion 警报表情标识
 * @param sound 可选的声音文件路径，为空则不播放
 */
void Application::Alert(const char *status, const char *message, const char *emotion, const std::string_view &sound) {
  ESP_LOGW(TAG, "Alert %s: %s [%s]", status, message, emotion);
  auto display = Board::GetInstance().GetDisplay();
  display->SetStatus(status);
  display->SetEmotion(emotion);
  display->SetChatMessage("system", message);
  if (!sound.empty()) {
    PlaySound(sound);
  }
}

/**
 * 当设备处于空闲状态时，清除屏幕上的警报信息。
 * 将显示状态设置为待机，表情设为中性，并清空系统聊天消息。
 */
void Application::DismissAlert() {
  if (device_state_ == kDeviceStateIdle) {
    auto display = Board::GetInstance().GetDisplay();
    display->SetStatus(Lang::Strings::STANDBY);
    display->SetEmotion("neutral");
    display->SetChatMessage("system", "");
  }
}

/**
 * @brief 播放音频数据
 *
 * 将输入的音频数据解析为Opus编码帧，并放入解码队列等待播放。
 *
 * @param sound 包含BinaryProtocol3头和Opus编码数据的音频数据视图
 *
 * @note 音频数据格式要求：
 * 1. 每个音频包必须以BinaryProtocol3头开始
 * 2. BinaryProtocol3头后跟随Opus编码的payload数据
 * 3. payload_size字段需使用网络字节序
 *
 * 函数内部会启用音频编解码器输出，并设置解码采样率为16kHz。
 * 解析后的Opus帧会被放入audio_decode_queue_队列，由解码线程处理。
 */
void Application::PlaySound(const std::string_view &sound) {
  auto codec = Board::GetInstance().GetAudioCodec();
  codec->EnableOutput(true);
  SetDecodeSampleRate(16000);
  const char *data = sound.data();
  size_t      size = sound.size();
  for (const char *p = data; p < data + size;) {
    // 每一个音频包都有一个 BinaryProtocol3 头结构，之后跟着一段 Opus 编码的 payload 音频数据。
    auto p3 = (BinaryProtocol3 *)p;
    p += sizeof(BinaryProtocol3);

    // payload_size 表示 Opus 数据体的大小（网络序转主机序）
    auto payload_size = ntohs(p3->payload_size);

    // 将这一段 Opus 数据提取出来，作为一个音频帧。
    std::vector<uint8_t> opus;
    opus.resize(payload_size);
    memcpy(opus.data(), p3->payload, payload_size);
    p += payload_size;

    // 把这个音频帧压入 audio_decode_queue_ 队列，等待解码器线程/任务去解码并播放。
    std::lock_guard<std::mutex> lock(mutex_);
    audio_decode_queue_.emplace_back(std::move(opus));
  }
}

/**
 * @brief 切换设备聊天状态
 *
 * 根据当前设备状态执行不同的操作：
 * - 如果设备正在激活(kDeviceStateActivating)，则设置为空闲状态(kDeviceStateIdle)
 * - 如果协议未初始化，记录错误日志
 * - 如果设备空闲(kDeviceStateIdle)，启动连接过程并开始监听
 * - 如果设备正在说话(kDeviceStateSpeaking)，中止说话
 * - 如果设备正在监听(kDeviceStateListening)，关闭音频通道
 *
 * 注意：所有状态变更操作都通过Schedule异步执行
 */
void Application::ToggleChatState() {
  if (device_state_ == kDeviceStateActivating) {
    SetDeviceState(kDeviceStateIdle);
    return;
  }

  if (!protocol_) {
    ESP_LOGE(TAG, "Protocol not initialized");
    return;
  }

  if (device_state_ == kDeviceStateIdle) {
    Schedule([this]() {
      SetDeviceState(kDeviceStateConnecting);
      if (!protocol_->OpenAudioChannel()) {
        return;
      }

      keep_listening_ = true;
      protocol_->SendStartListening(kListeningModeAutoStop);
      SetDeviceState(kDeviceStateListening);
    });
  } else if (device_state_ == kDeviceStateSpeaking) {
    Schedule([this]() { AbortSpeaking(kAbortReasonNone); });
  } else if (device_state_ == kDeviceStateListening) {
    Schedule([this]() { protocol_->CloseAudioChannel(); });
  }
}

/**
 * 开始监听音频输入
 *
 * 根据当前设备状态执行不同操作：
 * 1. 若设备正在激活中，则设置为空闲状态并返回
 * 2. 若协议未初始化，记录错误日志并返回
 * 3. 在空闲状态下：打开音频通道并发送开始监听指令
 * 4. 在说话状态下：中止当前说话并发送开始监听指令
 *
 * @note 所有操作都通过Schedule方法异步执行
 */
void Application::StartListening() {
  if (device_state_ == kDeviceStateActivating) {
    SetDeviceState(kDeviceStateIdle);
    return;
  }

  if (!protocol_) {
    ESP_LOGE(TAG, "Protocol not initialized");
    return;
  }

  keep_listening_ = false;
  if (device_state_ == kDeviceStateIdle) {
    Schedule([this]() {
      if (!protocol_->IsAudioChannelOpened()) {
        SetDeviceState(kDeviceStateConnecting);
        if (!protocol_->OpenAudioChannel()) {
          return;
        }
      }
      protocol_->SendStartListening(kListeningModeManualStop);
      SetDeviceState(kDeviceStateListening);
    });
  } else if (device_state_ == kDeviceStateSpeaking) {
    Schedule([this]() {
      AbortSpeaking(kAbortReasonNone);
      protocol_->SendStartListening(kListeningModeManualStop);
      SetDeviceState(kDeviceStateListening);
    });
  }
}

/**
 * 停止设备监听状态
 *
 * 通过调度任务执行以下操作：
 * 1. 检查当前设备状态是否为监听状态
 * 2. 如果是则发送停止监听协议
 * 3. 将设备状态设置为空闲状态
 */
void Application::StopListening() {
  Schedule([this]() {
    if (device_state_ == kDeviceStateListening) {
      protocol_->SendStopListening();
      SetDeviceState(kDeviceStateIdle);
    }
  });
}

/**
 * @brief 启动应用程序的主流程
 *
 * 初始化硬件设备（显示、音频编解码器），配置Opus编解码器参数，
 * 设置音频输入/输出回调，并创建主循环任务。
 * 根据不同的开发板类型设置不同的Opus编码复杂度。
 * 可选功能包括：音频处理、唤醒词检测、网络协议初始化等（通过宏定义控制）。
 * 最后将设备状态设置为空闲并启动时钟定时器。
 */
void Application::Start() {
  auto &board = Board::GetInstance();
  SetDeviceState(kDeviceStateStarting);

  /* Setup the display */
  auto display = board.GetDisplay();

  task_uart_init();

#if 1
  // 灯控制测试
  vTaskDelay(pdMS_TO_TICKS(1000));

  tm1652_set_display(true, 15, 4);
  vTaskDelay(pdMS_TO_TICKS(1000));
  uint8_t buf[7] = {0x08, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
  tm1652_multi_write(buf, sizeof(buf));
  vTaskDelay(pdMS_TO_TICKS(2000));
  tm1652_set_led(1, false); // 关闭第一个LED
  vTaskDelay(pdMS_TO_TICKS(1000));
  tm1652_set_led(2, false);
  vTaskDelay(pdMS_TO_TICKS(1000));
  tm1652_set_led(3, false);
  vTaskDelay(pdMS_TO_TICKS(1000));
  tm1652_set_led(4, false);
  vTaskDelay(pdMS_TO_TICKS(1000));
  tm1652_set_led(5, false);
  vTaskDelay(pdMS_TO_TICKS(1000));
  tm1652_set_led(6, false);
  vTaskDelay(pdMS_TO_TICKS(1000));
  tm1652_set_led(7, false);
  vTaskDelay(pdMS_TO_TICKS(1000));
  tm1652_set_led(8, false);

#endif

  /* Setup the audio codec */
  auto codec = board.GetAudioCodec();

  opus_decode_sample_rate_ = codec->output_sample_rate();
  opus_decoder_            = std::make_unique<OpusDecoderWrapper>(opus_decode_sample_rate_, 1);

  opus_encoder_ = std::make_unique<OpusEncoderWrapper>(16000, 1, OPUS_FRAME_DURATION_MS);
  // For ML307 boards, we use complexity 5 to save bandwidth
  // For other boards, we use complexity 3 to save CPU
  if (board.GetBoardType() == "ml307") {
    ESP_LOGI(TAG, "ML307 board detected, setting opus encoder complexity to 5");
    opus_encoder_->SetComplexity(5);
  } else {
    ESP_LOGI(TAG, "WiFi board detected, setting opus encoder complexity to 3");
    opus_encoder_->SetComplexity(3);
  }

  if (codec->input_sample_rate() != 16000) {
    input_resampler_.Configure(codec->input_sample_rate(), 16000);
    reference_resampler_.Configure(codec->input_sample_rate(), 16000);
  }
  codec->OnInputReady([this, codec]() {
    BaseType_t higher_priority_task_woken = pdFALSE;
    xEventGroupSetBitsFromISR(event_group_, AUDIO_INPUT_READY_EVENT, &higher_priority_task_woken);
    return higher_priority_task_woken == pdTRUE;
  });
  codec->OnOutputReady([this]() {
    BaseType_t higher_priority_task_woken = pdFALSE;
    xEventGroupSetBitsFromISR(event_group_, AUDIO_OUTPUT_READY_EVENT, &higher_priority_task_woken);
    return higher_priority_task_woken == pdTRUE;
  });
  codec->Start();

  /* Start the main loop */
  xTaskCreate(
      [](void *arg) {
        Application *app = (Application *)arg;
        app->MainLoop();
        vTaskDelete(NULL);
      },
      "main_loop",
      4096 * 2,
      this,
      4,
      nullptr);

#if 1
  /* Wait for the network to be ready */
  board.StartNetwork();
#endif

#if 1
  // Initialize the protocol
  display->SetStatus(Lang::Strings::LOADING_PROTOCOL);

#ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET
  protocol_ = std::make_unique<WebsocketProtocol>();
#else
  protocol_ = std::make_unique<MqttProtocol>();
#endif
  protocol_->OnNetworkError([this](const std::string &message) {
    SetDeviceState(kDeviceStateIdle);
    Alert(Lang::Strings::ERROR, message.c_str(), "sad", Lang::Sounds::P3_EXCLAMATION);
  });
  /* 注册语音数据回调，当接收到服务器端发送的语音数据时，将数据添加到音频解码队列中 */
  protocol_->OnIncomingAudio([this](std::vector<uint8_t> &&data) {
    std::lock_guard<std::mutex> lock(mutex_);
    if (device_state_ == kDeviceStateSpeaking) {
      audio_decode_queue_.emplace_back(std::move(data));
    }
  });
  protocol_->OnAudioChannelOpened([this, codec, &board]() {
    board.SetPowerSaveMode(false);
    if (protocol_->server_sample_rate() != codec->output_sample_rate()) {
      ESP_LOGW(
          TAG,
          "Server sample rate %d does not match device output sample rate %d, resampling may cause distortion",
          protocol_->server_sample_rate(),
          codec->output_sample_rate());
    }
    SetDecodeSampleRate(protocol_->server_sample_rate());
    auto &thing_manager = iot::ThingManager::GetInstance();
    protocol_->SendIotDescriptors(thing_manager.GetDescriptorsJson());
    std::string states;
    if (thing_manager.GetStatesJson(states, false)) {
      protocol_->SendIotStates(states);
    }
  });
  protocol_->OnAudioChannelClosed([this, &board]() {
    board.SetPowerSaveMode(true);
    Schedule([this]() {
      auto display = Board::GetInstance().GetDisplay();
      display->SetChatMessage("system", "");
      SetDeviceState(kDeviceStateIdle);
    });
  });
  protocol_->OnIncomingJson([this, display](const cJSON *root) {
    // Parse JSON data
    auto type = cJSON_GetObjectItem(root, "type");
    if (strcmp(type->valuestring, "tts") == 0) {
      auto state = cJSON_GetObjectItem(root, "state");
      if (strcmp(state->valuestring, "start") == 0) {
        Schedule([this]() {
          aborted_ = false;
          if (device_state_ == kDeviceStateIdle || device_state_ == kDeviceStateListening) {
            SetDeviceState(kDeviceStateSpeaking);
          }
        });
      } else if (strcmp(state->valuestring, "stop") == 0) {
        Schedule([this]() {
          if (device_state_ == kDeviceStateSpeaking) {
            background_task_->WaitForCompletion();
            if (keep_listening_) {
              protocol_->SendStartListening(kListeningModeAutoStop);
              SetDeviceState(kDeviceStateListening);
            } else {
              SetDeviceState(kDeviceStateIdle);
            }
          }
        });
      } else if (strcmp(state->valuestring, "sentence_start") == 0) {
        auto text = cJSON_GetObjectItem(root, "text");
        if (text != NULL) {
          ESP_LOGI(TAG, "<< %s", text->valuestring);
          Schedule([this, display, message = std::string(text->valuestring)]() {
            display->SetChatMessage("assistant", message.c_str());
          });
        }
      }
    } else if (strcmp(type->valuestring, "stt") == 0) {
      auto text = cJSON_GetObjectItem(root, "text");
      if (text != NULL) {
        ESP_LOGI(TAG, ">> %s", text->valuestring);
        Schedule([this, display, message = std::string(text->valuestring)]() {
          display->SetChatMessage("user", message.c_str());
        });
      }
    } else if (strcmp(type->valuestring, "llm") == 0) {
      auto emotion = cJSON_GetObjectItem(root, "emotion");
      if (emotion != NULL) {
        Schedule([this, display, emotion_str = std::string(emotion->valuestring)]() {
          display->SetEmotion(emotion_str.c_str());
        });
      }
    } else if (strcmp(type->valuestring, "iot") == 0) {
      auto commands = cJSON_GetObjectItem(root, "commands");
      if (commands != NULL) {
        auto &thing_manager = iot::ThingManager::GetInstance();
        for (int i = 0; i < cJSON_GetArraySize(commands); ++i) {
          auto command = cJSON_GetArrayItem(commands, i);
          thing_manager.Invoke(command);
        }
      }
    } else if (strcmp(type->valuestring, "esp32_rgb25_Control") == 0) {

      // 获取 led_count
      cJSON *led_count_item = cJSON_GetObjectItem(root, "led_count");
      int    led_count      = 0;
      if (cJSON_IsNumber(led_count_item)) {
        led_count = led_count_item->valueint;
      }

      // 获取 leds 数组
      cJSON *leds = cJSON_GetObjectItem(root, "leds");
      if (cJSON_IsArray(leds)) {
        int size = cJSON_GetArraySize(leds);

        for (int i = 0; i < size; i++) {
          cJSON *led_state = cJSON_GetArrayItem(leds, i);
          if (cJSON_IsBool(led_state)) {
            bool on = cJSON_IsTrue(led_state);

            // 调用 LED 控制函数（编号从 1 开始）
            tm1652_set_led(i + 1, on);

            ESP_LOGD(TAG, "LED %d -> %s\n", i + 1, on ? "ON" : "OFF");
          }
        }
      }
    }
  });
  protocol_->Start();

#endif
  ESP_LOGI(TAG, "protocol_->Start()");
  vTaskDelay(pdMS_TO_TICKS(500));

#if 0
  // Check for new firmware version or get the MQTT broker address
  ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
  ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
  ota_.SetHeader("Client-Id", board.GetUuid());
  ota_.SetHeader("Accept-Language", Lang::CODE);
  auto app_desc = esp_app_get_description();
  ota_.SetHeader("User-Agent", std::string(BOARD_NAME "/") + app_desc->version);

  xTaskCreate(
      [](void *arg) {
        Application *app = (Application *)arg;
        app->CheckNewVersion();
        vTaskDelete(NULL);
      },
      "check_new_version",
      4096 * 2,
      this,
      2,
      nullptr);

#endif

#if CONFIG_USE_AUDIO_PROCESSOR
  audio_processor_.Initialize(codec->input_channels(), codec->input_reference());
  audio_processor_.OnOutput([this](std::vector<int16_t> &&data) {
    background_task_->Schedule([this, data = std::move(data)]() mutable {
      opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t> &&opus) {
        Schedule([this, opus = std::move(opus)]() { protocol_->SendAudio(opus); });
      });
    });
  });
  audio_processor_.OnVadStateChange([this](bool speaking) {
    if (device_state_ == kDeviceStateListening) {
      Schedule([this, speaking]() {
        if (speaking) {
          voice_detected_ = true;
        } else {
          voice_detected_ = false;
        }
        auto led = Board::GetInstance().GetLed();
        led->OnStateChanged();
      });
    }
  });
#endif

#if CONFIG_USE_WAKE_WORD_DETECT
  wake_word_detect_.Initialize(codec->input_channels(), codec->input_reference());
  wake_word_detect_.OnWakeWordDetected([this](const std::string &wake_word) {
    Schedule([this, &wake_word]() {
      if (device_state_ == kDeviceStateIdle) {
#if 1
        SetDeviceState(kDeviceStateConnecting);
        wake_word_detect_.EncodeWakeWordData();

        if (!protocol_->OpenAudioChannel()) {
          wake_word_detect_.StartDetection();
          return;
        }

        std::vector<uint8_t> opus;
        // Encode and send the wake word data to the server
        while (wake_word_detect_.GetWakeWordOpus(opus)) {
          protocol_->SendAudio(opus);
        }
        // Set the chat state to wake word detected
        protocol_->SendWakeWordDetected(wake_word);
        ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
        keep_listening_ = true;
#endif
        SetDeviceState(kDeviceStateIdle);
      } else if (device_state_ == kDeviceStateSpeaking) {
        AbortSpeaking(kAbortReasonWakeWordDetected);
      } else if (device_state_ == kDeviceStateActivating) {
        SetDeviceState(kDeviceStateIdle);
      }
    });
  });
  wake_word_detect_.StartDetection();
#endif

  SetDeviceState(kDeviceStateIdle);
  esp_timer_start_periodic(clock_timer_handle_, 1000000);
}

/**
 * @brief 处理时钟定时器事件
 *
 * 每10秒执行以下操作：
 * 1. 打印当前内部SRAM的可用空间和最小可用空间
 * 2. 如果设备处于空闲状态且已同步服务器时间，则在显示屏上显示当前时间(HH:MM格式)
 *
 * @note 该函数通过clock_ticks_计数器来控制每10秒执行一次操作
 * @note 使用heap_caps_get_free_size和heap_caps_get_minimum_free_size获取内存信息
 * @note 使用Schedule方法异步更新显示屏状态
 */
void Application::OnClockTimer() {
  clock_ticks_++;

  // Print the debug info every 10 seconds
  if (clock_ticks_ % 30 == 0) {
    // SystemInfo::PrintRealTimeStats(pdMS_TO_TICKS(1000));
    int free_sram     = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
    int min_free_sram = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
    ESP_LOGI(TAG, "Free internal: %u minimal internal: %u", free_sram, min_free_sram);

    // If we have synchronized server time, set the status to clock "HH:MM" if the device is idle
    if (ota_.HasServerTime()) {
      if (device_state_ == kDeviceStateIdle) {
        Schedule([this]() {
          // Set status to clock "HH:MM"
          time_t now = time(NULL);
          char   time_str[64];
          strftime(time_str, sizeof(time_str), "%H:%M  ", localtime(&now));
          Board::GetInstance().GetDisplay()->SetStatus(time_str);
        });
      }
    }
  }
}

/**
 * @brief 调度一个回调函数到主任务队列
 *
 * 线程安全地将回调函数添加到主任务队列，并设置调度事件标志位。
 *
 * @param callback 要调度的无参回调函数
 */
void Application::Schedule(std::function<void()> callback) {
  {
    std::lock_guard<std::mutex> lock(mutex_);
    main_tasks_.push_back(std::move(callback));
  }
  xEventGroupSetBits(event_group_, SCHEDULE_EVENT);
}

// The Main Loop controls the chat state and websocket connection
// If other tasks need to access the websocket or chat state,
// they should use Schedule to call this function
void Application::MainLoop() {
  while (true) {
    auto bits = xEventGroupWaitBits(
        event_group_,
        SCHEDULE_EVENT | AUDIO_INPUT_READY_EVENT | AUDIO_OUTPUT_READY_EVENT,
        pdTRUE,
        pdFALSE,
        portMAX_DELAY);

    if (bits & AUDIO_INPUT_READY_EVENT) {
      InputAudio();
    }
    if (bits & AUDIO_OUTPUT_READY_EVENT) {
      OutputAudio();
    }
    if (bits & SCHEDULE_EVENT) {
      std::unique_lock<std::mutex>     lock(mutex_);
      std::list<std::function<void()>> tasks = std::move(main_tasks_);
      lock.unlock();
      for (auto &task : tasks) {
        task();
      }
    }
  }
}

/**
 * 重置解码器状态
 *
 * 线程安全操作，会清空音频解码队列并重置解码器内部状态
 * 同时更新最后输出时间为当前时间
 */
void Application::ResetDecoder() {
  std::lock_guard<std::mutex> lock(mutex_);
  opus_decoder_->ResetState();
  audio_decode_queue_.clear();
  last_output_time_ = std::chrono::steady_clock::now();
}

/**
 * 输出音频数据到音频编解码器
 *
 * 该函数负责从音频解码队列中获取数据，解码并重采样后输出到音频设备。
 * 当队列为空且设备处于空闲状态超过指定时间时，会禁用音频输出。
 * 在监听状态下会清空队列不进行输出。
 *
 * 主要流程：
 * 1. 检查队列状态，处理长时间无数据的情况
 * 2. 在监听状态下清空队列
 * 3. 从队列获取数据并解码
 * 4. 根据需要进行重采样
 * 5. 输出到音频设备
 */
void Application::OutputAudio() {
  auto now   = std::chrono::steady_clock::now();
  auto codec = Board::GetInstance().GetAudioCodec();

  const int max_silence_seconds = 10;

  std::unique_lock<std::mutex> lock(mutex_);
  if (audio_decode_queue_.empty()) {
    // Disable the output if there is no audio data for a long time
    if (device_state_ == kDeviceStateIdle) {
      // 计算当前时间 now 和之前时间 last_output_time_ 之间的时间差（以“秒”为单位），并获取这个差值的整数数值。
      auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - last_output_time_).count();
      if (duration > max_silence_seconds) {
        codec->EnableOutput(false);
      }
    }
    return;
  }

  if (device_state_ == kDeviceStateListening) {
    audio_decode_queue_.clear();
    return;
  }

  last_output_time_ = now;
  auto opus         = std::move(audio_decode_queue_.front());

  audio_decode_queue_.pop_front();
  lock.unlock();

  background_task_->Schedule([this, codec, opus = std::move(opus)]() mutable {
    if (aborted_) {
      return;
    }

    // 调用一个 Opus 解码器对象 opus_decoder_ 的 Decode() 函数来将 OPUS 编码的数据解码为 PCM 格式音频
    std::vector<int16_t> pcm;
    if (!opus_decoder_->Decode(std::move(opus), pcm)) {
      return;
    }

    // Resample if the sample rate is different
    if (opus_decode_sample_rate_ != codec->output_sample_rate()) {
      int                  target_size = output_resampler_.GetOutputSamples(pcm.size());
      std::vector<int16_t> resampled(target_size);
      output_resampler_.Process(pcm.data(), pcm.size(), resampled.data());
      pcm = std::move(resampled);
    }

    codec->OutputData(pcm);
  });
}

/**
 * @brief 处理音频输入数据
 *
 * 从音频编解码器获取输入数据，并根据需要进行重采样处理。支持双声道分离处理。
 * 根据配置将处理后的数据送入唤醒词检测模块或音频处理器，或直接编码为Opus格式上传云端。
 *
 * 主要功能：
 * 1. 获取音频输入数据
 * 2. 检查采样率并进行重采样（16kHz）
 * 3. 处理双声道数据（麦克风声道和参考声道）
 * 4. 根据配置分发处理后的音频数据
 */
void Application::InputAudio() {

  auto codec = Board::GetInstance().GetAudioCodec();

  std::vector<int16_t> data;
  if (!codec->InputData(data)) {
    return;
  }
  /**
   * 这是判断 麦克风实际输入的采样率 和 后续模块要求的 16kHz 是否一致。
   * 很多语音模型或编码器（如 Opus）默认输入 16kHz，因此此处有必要做重采样（resample）
   */
  if (codec->input_sample_rate() != 16000) {
    if (codec->input_channels() == 2) { /* 如果是双声道 */
      auto mic_channel       = std::vector<int16_t>(data.size() / 2);
      auto reference_channel = std::vector<int16_t>(data.size() / 2);
      for (size_t i = 0, j = 0; i < mic_channel.size(); ++i, j += 2) {
        mic_channel[i]       = data[j];
        reference_channel[i] = data[j + 1];
      }
      auto resampled_mic       = std::vector<int16_t>(input_resampler_.GetOutputSamples(mic_channel.size()));
      auto resampled_reference = std::vector<int16_t>(reference_resampler_.GetOutputSamples(reference_channel.size()));
      input_resampler_.Process(mic_channel.data(), mic_channel.size(), resampled_mic.data());
      reference_resampler_.Process(reference_channel.data(), reference_channel.size(), resampled_reference.data());
      data.resize(resampled_mic.size() + resampled_reference.size());
      for (size_t i = 0, j = 0; i < resampled_mic.size(); ++i, j += 2) {
        data[j]     = resampled_mic[i];
        data[j + 1] = resampled_reference[i];
      }
    } else {
      auto resampled = std::vector<int16_t>(input_resampler_.GetOutputSamples(data.size()));
      input_resampler_.Process(data.data(), data.size(), resampled.data());
      data = std::move(resampled);
    }
  }

#if CONFIG_USE_WAKE_WORD_DETECT
  /* 喂入唤醒词检测模块 */
  if (wake_word_detect_.IsDetectionRunning()) {
    wake_word_detect_.Feed(data);
  }
#endif
#if CONFIG_USE_AUDIO_PROCESSOR
  /* 喂入音频前处理模块（降噪、AGC等） */
  if (audio_processor_.IsRunning()) {
    audio_processor_.Input(data);
  }
#else
  /* 直接编码为 Opus 上传云端 */
  if (device_state_ == kDeviceStateListening) {
    background_task_->Schedule([this, data = std::move(data)]() mutable {
      opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t> &&opus) {
        Schedule([this, opus = std::move(opus)]() { protocol_->SendAudio(opus); });
      });
    });
  }
#endif
}

/**
 * 中止当前语音播放
 *
 * @param reason 中止原因，用于传递给协议层
 * @note 该方法会设置aborted_标志位，并通过协议层发送中止指令
 */
void Application::AbortSpeaking(AbortReason reason) {
  ESP_LOGI(TAG, "Abort speaking");
  aborted_ = true;
  protocol_->SendAbortSpeaking(reason);
}

/**
 * @brief 设置设备状态并执行相应操作
 *
 * 根据传入的设备状态参数更新当前设备状态，并执行与该状态相关的操作：
 * - 记录状态变更日志
 * - 等待后台任务完成
 * - 更新外设状态（LED、显示屏等）
 * - 根据状态启停音频处理器和唤醒词检测
 * - 处理状态切换时的特殊逻辑（如从Speaking切换到Listening时的延迟）
 *
 * @param state 要设置的新设备状态
 */
void Application::SetDeviceState(DeviceState state) {
  if (device_state_ == state) {
    return;
  }

  clock_ticks_        = 0;
  auto previous_state = device_state_;
  device_state_       = state;
  ESP_LOGI(TAG, "STATE: %s", STATE_STRINGS[device_state_]);
  // The state is changed, wait for all background tasks to finish
  background_task_->WaitForCompletion();

  auto &board   = Board::GetInstance();
  auto  codec   = board.GetAudioCodec();
  auto  display = board.GetDisplay();
  auto  led     = board.GetLed();
  led->OnStateChanged();
  switch (state) {
  case kDeviceStateUnknown:
  case kDeviceStateIdle:
    display->SetStatus(Lang::Strings::STANDBY);
    display->SetEmotion("neutral");
#if CONFIG_USE_AUDIO_PROCESSOR
    audio_processor_.Stop();
#endif
#if CONFIG_USE_WAKE_WORD_DETECT
    wake_word_detect_.StartDetection();
#endif
    break;
  case kDeviceStateConnecting:
    display->SetStatus(Lang::Strings::CONNECTING);
    display->SetEmotion("neutral");
    display->SetChatMessage("system", "");
    break;
  case kDeviceStateListening:
    display->SetStatus(Lang::Strings::LISTENING);
    display->SetEmotion("neutral");
    ResetDecoder();
    opus_encoder_->ResetState();
#if CONFIG_USE_AUDIO_PROCESSOR
    audio_processor_.Start();
#endif
#if CONFIG_USE_WAKE_WORD_DETECT
    wake_word_detect_.StopDetection();
#endif
    UpdateIotStates();
    if (previous_state == kDeviceStateSpeaking) {
      // FIXME: Wait for the speaker to empty the buffer
      vTaskDelay(pdMS_TO_TICKS(120));
    }
    break;
  case kDeviceStateSpeaking:
    display->SetStatus(Lang::Strings::SPEAKING);
    ResetDecoder();
    codec->EnableOutput(true);
#if CONFIG_USE_AUDIO_PROCESSOR
    audio_processor_.Stop();
#endif
#if CONFIG_USE_WAKE_WORD_DETECT
    wake_word_detect_.StartDetection();
#endif
    break;
  default:
    // Do nothing
    break;
  }
}

/**
 * @brief 设置Opus解码器的采样率
 *
 * 如果新采样率与当前采样率不同，则重新初始化Opus解码器。
 * 如果新采样率与音频编解码器输出采样率不同，则配置输出重采样器。
 *
 * @param sample_rate 要设置的采样率（单位：Hz）
 */
void Application::SetDecodeSampleRate(int sample_rate) {
  if (opus_decode_sample_rate_ == sample_rate) {
    return;
  }

  opus_decode_sample_rate_ = sample_rate;
  opus_decoder_.reset();
  opus_decoder_ = std::make_unique<OpusDecoderWrapper>(opus_decode_sample_rate_, 1);

  auto codec = Board::GetInstance().GetAudioCodec();
  if (opus_decode_sample_rate_ != codec->output_sample_rate()) {
    ESP_LOGI(TAG, "Resampling audio from %d to %d", opus_decode_sample_rate_, codec->output_sample_rate());
    output_resampler_.Configure(opus_decode_sample_rate_, codec->output_sample_rate());
  }
}

/**
 * @brief 更新并发送IoT设备状态
 *
 * 从ThingManager获取当前所有IoT设备的状态（JSON格式），
 * 并通过协议层发送这些状态信息。
 *
 * @note 仅在成功获取状态信息时才会触发发送操作
 */
void Application::UpdateIotStates() {
  auto       &thing_manager = iot::ThingManager::GetInstance();
  std::string states;
  if (thing_manager.GetStatesJson(states, true)) {
    protocol_->SendIotStates(states);
  }
}

/**
 * @brief 重启设备
 *
 * 记录重启日志并调用ESP32的esp_restart()函数执行系统重启
 */
void Application::Reboot() {
  ESP_LOGI(TAG, "Rebooting...");
  esp_restart();
}

/**
 * 处理唤醒词触发事件
 *
 * 根据设备当前状态执行不同操作：
 * - 空闲状态：切换聊天状态并发送唤醒词检测通知
 * - 说话状态：中止当前说话
 * - 监听状态：关闭音频通道
 *
 * @param wake_word 检测到的唤醒词字符串
 */
void Application::WakeWordInvoke(const std::string &wake_word) {
  if (device_state_ == kDeviceStateIdle) {
    ToggleChatState();
    Schedule([this, wake_word]() {
      if (protocol_) {
        protocol_->SendWakeWordDetected(wake_word);
      }
    });
  } else if (device_state_ == kDeviceStateSpeaking) {
    Schedule([this]() { AbortSpeaking(kAbortReasonNone); });
  } else if (device_state_ == kDeviceStateListening) {
    Schedule([this]() {
      if (protocol_) {
        protocol_->CloseAudioChannel();
      }
    });
  }
}

/**
 * 检查设备是否可以进入睡眠模式
 *
 * @return true 如果设备处于空闲状态且没有音频通道打开，可以进入睡眠模式
 * @return false 如果设备不处于空闲状态或有音频通道打开，不能进入睡眠模式
 */
bool Application::CanEnterSleepMode() {
  if (device_state_ != kDeviceStateIdle) {
    return false;
  }

  if (protocol_ && protocol_->IsAudioChannelOpened()) {
    return false;
  }

  // Now it is safe to enter sleep mode
  return true;
}
